# -*- coding: utf-8 -*-

import os
import random
from select import select
import socket
from queue import Empty, Queue
import threading
from typing import Any, Iterable
from multiprocessing.managers import SyncManager

from contract_market.error.error import QueueExitError
from contract_market.utils.utils import virtual
from contract_market.utils.singleton import SingleMeta

class PollableQueue(Queue):
    """ """

    def __init__(self, maxsize: int = 0) -> None:
        super().__init__(maxsize)
        if os.name == "posix":
            self._putsocket, self._getsocket = socket.socketpair()
        else:
            server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            server.bind(("localhost", 0))
            server.listen(1)

            self._putsocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self._putsocket.connect(server.getsockname())
            self._getsocket, _ = server.accept()

    def get(self):
        """ """
        self._getsocket.recv(1)
        return super().get()

    def fileno(self):
        return self._getsocket.fileno()

    def send(self, msg):
        """ """
        super().put(msg)
        self._putsocket.send(b'x')

    def __del__(self):
        self._putsocket.close()
        self._getsocket.close()


# class SyncQueue(Queue):
#     def get_attribute(self, name):
#         return getattr(self, name)

# class syncManager(SyncManager):
#     pass        

# def get_manager():
#     syncManager.register("Queue", SyncQueue)
#     m = syncManager()
#     m.start()
#     return m

class Actors(SingleMeta):

    def __init__(self, maxsize=100) -> None:
        """ """
        # syncManager = get_manager()
        self._queue = Queue(maxsize=maxsize)    # 
        self._active = False
        self._consumer = threading.Thread(
            target=self._bootstrap, daemon=True, name="Actors")

    def start(self):
        """ """
        self._active = True
        self._consumer.start()

    def stop(self):
        """ """
        self.send(QueueExitError)
        self._active = False
        self._consumer.join()
        del self._queue

    def _bootstrap(self):
        """ """
        while self._active:
            self.run()

    def qsize(self):
        return self._queue.qsize()

    @virtual
    def run(self):
        """ """
        try:
            v = self.recv()
            if v is QueueExitError:
                return
            else:
                return v
        except StopIteration as e:
            pass
        except Empty:
            pass

    def send(self, msg: Any):
        self._queue.put(msg)

    def recv(self) -> Iterable:
        return self._queue.get()


class MultiActors:
    MAX_WORKS = 5

    def __init__(self) -> None:
        """ """
        self._queues = [PollableQueue() for _ in range(self.MAX_WORKS)]
        self._lock = threading.Lock()
        self._active = False
        self._consumer = threading.Thread(
            target=self._bootstrap, daemon=True, name="MultiActors")

    def start(self):
        """ """
        self._active = True
        self._consumer.start()

    def stop(self):
        """ """
        self.send(QueueExitError)
        self._active = False
        self._consumer.join()
        del self._queues

    def _bootstrap(self):
        """ """
        while self._active:
            self.run()

    @virtual
    def run(self):
        """ """
        try:
            v = self.recv()
            if v is QueueExitError:
                return
        except StopIteration as e:
            pass
        except Empty:
            pass

    def recv(self) -> Iterable:
        can_r, _, _ = select(self._queues, [], [])
        for q in can_r:
            yield q.get()

    def send(self, msg: Any):
        with self._lock:
            q: PollableQueue = random.choice(self._queues)
            q.send(msg)
